#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <libgen.h>
#include "sharedseg.h"
#include "nPatriciaTrie.h"
#include "linkedlist.h"
#include "tagdefs.h"
#include "tagserver.h"
#include "fileperm.h"
#include "stopwatch.h"
//#include "inode.h"

#define NOIPCNOTIFICATION
#define NOVERBOSE

#define MAXFILENAMELENGTH 300

bool booting;

/*!
 * Main function for server ...
 */
int main(int argc, char *argv[]) 
{

	char message[350];
	linkedlist *suggestlist=NULL;
	stopwatch sttime;
	sttime.start();
        key_t key;
	bool shutdown;
	semid=-1;
	shmid=-1;	

	/*! - Checks for custom index file is given as argument */
	if (argc>=2) {
//		#undef INDEX_FILE
			
				
			strcpy(customindex,argv[1]);
			printf("Using custom index file : %s\n",customindex);
	}
	else
	{
		strcpy(customindex, INDEX_FILE);
	}

        key = ftok(TEXT, CHAR);
	booting=true;
	shutdown=false;
	printf("Initializing Server\n");
        if (signal(SIGINT,sigint_handler)==SIG_ERR) {
		perror("signal");
		exit(1);
	}
        if (signal(SIGQUIT,sigint_handler)==SIG_ERR) {
		perror("signal");
		exit(1);
	}        
        if (signal(SIGTERM,sigint_handler)==SIG_ERR) {
		perror("signal");
		exit(1);
	}        

	/*! - Sets up the 3 semaphores with read&write permissions 666 */
        semid = semget(key, 3, 0666 | IPC_CREAT);
        if (semid < 0) {
                printf("Error in creating semaphore \n");
                exit(1);
        }
	
	#ifndef NOIPCNOTIFICATION
	printf("Semaphore Created!!!\n");
	#endif
	
        sarray[client_sem]=0;       // 0 means can work
	sarray[server_sem]=1;       // 1 means waiting for signal
	sarray[anyclient]=0;        // has anybody connected already? 
	
	if ((semctl(semid,1,SETALL,sarray))==-1) {
		printf("semctl init failed!\n");
		exit(1);
	}
	#ifndef NOIPCNOTIFICATION
	printf("Semactl init success!!!\n");
	#endif
	
	/*! - Creates shared memory to be used for communication */
        if((shmid = shmget(key, SEGSIZE, IPC_CREAT|IPC_EXCL|0666)) == -1)
        {
                printf("Shared memory segment exists!! Another server instance running ??\n");
		//exit(1);
		printf("Ignoring previos session. Creating new instance\n");
		shmid=shmget(key,SEGSIZE,0666);
        }
        
	#ifndef NOIPCNOTIFICATION
	printf("Shared memory segment Created!!!\n");
	#endif

	/*! - Gets the shared memory segment pointer */
        if((segptr = (struct shared*) shmat(shmid, 0, 0)) == NULL)
        {
                perror("shmat");
                exit(1);
        }
        
	#ifndef NOIPCNOTIFICATION
	printf("Memory segment associated with data!!!\n");
	#endif
	readfromfile();
	booting=false;
	sttime.stop();
	printf("Start Server took %d seconds\n",(int)sttime.reading());
	
	/*! - Waits for clients to connect and serve them */
       	segptr->abort=false; 
	while(1) {

		/*! - Wait for semaphore to get new client connection */ 
		#ifndef NOIPCNOTIFICATION
		printf("Waiting for semaphore(new client connection)!!!\n");
		#endif
        
		sem_wait(server_sem);

		// Got Semaphore 
		#ifndef NOIPCNOTIFICATION
		printf("Client connected!!!\n");
		#endif
       		
//      	if (strlen(segptr->command) != 0)
//		printf("Command Recieved : %s %s %s\n",segptr->command,segptr->param1,segptr->param2);
	
		if (segptr->abort) {
			#ifndef NOIPCNOTIFICATION
			printf("Client aborted!!\n");
			#endif
       			strcpy(segptr->command,"");
        		sarray[client_sem]=0; 
			sarray[server_sem]=1;
			sarray[anyclient]=0; 	
			continue;
		}
		linkedlist *result;
		result=NULL;

		/*! - Check for the command and perform the specified operation */ 

		/*! - randomtag --> randomTag() */
		if (strcmp(segptr->command,"randomtag")==0) {
			char *k;
			k=randomTag();
			result=new linkedlist();
			result->add(k);
			delete k;
		}
		/*! - randomfile --> randomFile() */
		if (strcmp(segptr->command,"randomfile")==0) {
			char *k;
			k=randomFile();
			result=new linkedlist();
			result->add(k);
			delete k;
		}

		/*! - tagtriesize --> Gives the number of nodes in the Tag Trie */
		if (strcmp(segptr->command,"tagtriesize")==0) {
			sprintf(message,"Find Tag Trie Size");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			sprintf(message,"Size %ld\n",Tags->triesize);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			sprintf(message,"%ld",Tags->triesize);
			strcpy(segptr->param2,message);
		}

		/*! - filetriesize --> Gives the number of nodes in the File Trie */
		if (strcmp(segptr->command,"filetriesize")==0) {
			sprintf(message,"Find File Trie Size");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			sprintf(message,"Size %ld\n",File->triesize);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			sprintf(message,"%ld",File->triesize);
			strcpy(segptr->param2,message);
		}

		/*! - add --> Adds new tag to a file */
		if (strcmp(segptr->command,"add")==0) {
			sprintf(message,"Add tags to file : %s\n",segptr->param1);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			sprintf(message,"\tAdding %s \t : %s \n",segptr->param2,(addtag(segptr->param1,segptr->param2))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		/*! - remove --> Removes a tag from file */
		if (strcmp(segptr->command,"remove")==0) {
			sprintf(message,"Remove tags to file : %s\n",segptr->param1);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			sprintf(message,"\tRemoving %s \t : %s \n",segptr->param2,(removetag(segptr->param1,segptr->param2))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		/*! - listtags --> lists all tags for a given file */
		else if (strcmp(segptr->command,"listtags")==0) {
			sprintf(message,"List tags of file : %s\n",segptr->param1);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			result=listtags(segptr->param1);
			sprintf(message,"%s\n",((result!=NULL)&&(!(result->isEmpty())))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		/*! - listfiles --> lists all files for a given tag */
		else if (strcmp(segptr->command,"listfiles")==0) {
			sprintf(message,"List files with tag : %s\n",segptr->param1);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			result=listfiles(segptr->param1);
			sprintf(message,"%s\n",((result!=NULL)&&(!(result->isEmpty())))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}
		
		/*! - listallfiles --> lists all files that are tagged */
		else if (strcmp(segptr->command,"listallfiles")==0) {
			sprintf(message,"List all files in structure \n");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			result=File->getList();
			sprintf(message,"%s\n",((result!=NULL)&&(!(result->isEmpty())))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		/*! - listalltags --> lists all tags that are used */
		else if (strcmp(segptr->command,"listalltags")==0) {
			sprintf(message,"List all tags in structure \n");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			result=Tags->getList();
			sprintf(message,"%s\n",((result!=NULL)&&(!(result->isEmpty())))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}
		
		/*! - getslashlist --> lists all directory tags only */
		else if (strcmp(segptr->command,"getslashlist")==0) {
			sprintf(message,"List all directory tags\n");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			result=slashlist();
			sprintf(message,"%s\n",((result!=NULL)&&(!(result->isEmpty())))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		/*! - removefile --> Remove a file */ 
		else if (strcmp(segptr->command,"removefile")==0) {
			sprintf(message,"Remove file : %s\n",segptr->param1);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			sprintf(message,"%s\n",(removefile(segptr->param1))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		// Copy a file
		else if (strcmp(segptr->command,"copyfile")==0) {
			sprintf(message,"Copy file %s ---> %s\n",segptr->param1,segptr->param2);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif

			struct stat mode;
			lstat(segptr->param1,&mode);
			if(DIRECTORY(mode.st_mode)) {
				// Source file is actually a directory
				
				// Both should end in "/" or both should not
				if ((segptr->param1[strlen(segptr->param1)-1])=='/') {
					if((segptr->param2[strlen(segptr->param2)-1])!='/')
						strcat(segptr->param2,"/");
				} else {
					if((segptr->param2[strlen(segptr->param2)-1])=='/')
						strcat(segptr->param1,"/");
				}
				linkedlist *filez;
				filez = File->getPartialList(segptr->param1);
				
				char masssource[MAXFILENAMELENGTH],massdest[MAXFILENAMELENGTH];
				char tempsource[MAXFILENAMELENGTH];
				int te;
				int ok=0;
				#ifndef NOVERBOSE
				printf("Copy Set of Files\n");
				#endif
				char *tmpch;
				while (!filez->isEmpty()) {
					strcpy(massdest,segptr->param2);
					tmpch=filez->remove();
					strcpy(masssource,tmpch);
					free(tmpch);
					strcpy(tempsource,masssource);
					for (te=0;te<strlen(masssource)-strlen(segptr->param1);te++)
						masssource[te]=masssource[te+strlen(segptr->param1)];
					masssource[te]='\0';
					strcat(massdest,masssource);
					if (!(copyfile(tempsource,massdest)))
						ok=-1;
					#ifndef NOVERBOSE
					printf("\t%s\t-> %s\n",tempsource,massdest);
					#endif
				}
			    sprintf(message,"%s\n",(ok==0)?"SUCCESS":"FAILED");
			    if (filez!=NULL) {
				    filez->removeAll();
				    delete filez;
				    filez=NULL;
			    }
			} else
			    sprintf(message,"%s\n",(copyfile(segptr->param1,segptr->param2))?"SUCCESS":"FAILED");

			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		// Move a file
		else if (strcmp(segptr->command,"movefile")==0) {
			sprintf(message,"Move file %s ---> %s\n",segptr->param1,segptr->param2);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif

			struct stat mode;
			/*
			lstat(segptr->param2,&mode);
			if (DIRECTORY(mode.st_mode)) {
				// Resultant is a directory  (req from GUI Konqueror)
				// append the file name of param1 to the param2
				//
				char tmp[300];
				strcpy(tmp,segptr->param2);
				if (tmp[strlen(tmp)-1]!='/')
					strcat(tmp,"/");
				strcat(tmp,basename(segptr->param1));
				strcpy(segptr->param2,tmp);
				#ifndef NOVERBOSE
				printf("Resultant is a dir (from GUI Konqueror\n");
				printf("New Result : %s\n",segptr->param2);
				#endif
			}*/
			if(lstat(segptr->param1,&mode)==ENOENT) {
				#ifndef NOVERBOSE
				printf("Source file not found ??\n");
				#endif

			} else
			if(DIRECTORY(mode.st_mode)) {
				// Source file is actually a directory
				
				// Both should end in "/" or both should not
				if ((segptr->param1[strlen(segptr->param1)-1])=='/') {
					if((segptr->param2[strlen(segptr->param2)-1])!='/')
						strcat(segptr->param2,"/");
				} else {
					if((segptr->param2[strlen(segptr->param2)-1])=='/')
						strcat(segptr->param1,"/");
				}
				linkedlist *filez;
				filez = File->getPartialList(segptr->param1);
				
				char masssource[MAXFILENAMELENGTH],massdest[MAXFILENAMELENGTH];
				char tempsource[MAXFILENAMELENGTH];
				int te;
				int ok=0;
				#ifndef NOVERBOSE
				printf("Move Set of Files\n");
				#endif
				char *tmpch;
				while ((filez!=NULL) && (!filez->isEmpty())) {
					strcpy(massdest,segptr->param2);
					tmpch=filez->remove();
					strcpy(masssource,tmpch);
					free(tmpch);
					strcpy(tempsource,masssource);
					for (te=0;te<strlen(masssource)-strlen(segptr->param1);te++)
					     masssource[te]=masssource[te+strlen(segptr->param1)];
					masssource[te]='\0';
					strcat(massdest,masssource);
					if (!(movefile(tempsource,massdest)))
						ok=-1;
					#ifndef NOVERBOSE
					printf("\t%s\t-> %s\n",tempsource,massdest);
					#endif
				}
			    sprintf(message,"%s\n",(ok==0)?"SUCCESS":"FAILED");
			    if (filez!=NULL) {
				    filez->removeAll();
				    delete filez;
				    filez=NULL;
			    }
			} else
			    sprintf(message,"%s\n",(movefile(segptr->param1,segptr->param2))?"SUCCESS":"FAILED");

			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		// Remove recursively file 
		else if (strcmp(segptr->command,"removefilerecurse")==0) {
			sprintf(message,"Remove recursively file %s\n",segptr->param1);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			
			struct stat mode;
			lstat(segptr->param1,&mode);
			if(DIRECTORY(mode.st_mode)) {
				linkedlist *filez;
				filez = File->getPartialList(segptr->param1);
				
				int ok=0;
				#ifndef NOVERBOSE
				printf("remove set of Files\n");
				#endif
				char *tmpsrc;
				while (!filez->isEmpty()) {
					tmpsrc=filez->remove();
					if (!(removefile(tmpsrc)))
						ok=-1;
					#ifndef NOVERBOSE
					printf("\t%s\n",tmpsrc);
					#endif
				}
			    sprintf(message,"%s\n",(ok==0)?"SUCCESS":"FAILED");
			    if (filez!=NULL) {
				    filez->removeAll();
				    delete filez;
				    filez=NULL;
			    }
		        }  else
			    sprintf(message,"%s\n",(removefile(segptr->param1))?"SUCCESS":"FAILED");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}

		// Flat Search 
		else if ( (strcmp(segptr->command,FLATSEARCH)==0) || 
			  (strcmp(segptr->command,HIERARCHICAL)==0) ) {
			linkedlist *knowntags=new linkedlist();
			linkedlist *starlist=NULL;
			linkedlist *files=NULL;
			sprintf(message,"Search for files\n");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			char *ptr,*pch;
			char dup[200];
			char tags[300];
			char *tmpch;
			strcpy(tags,segptr->param1);
			if (!((strcmp(tags,"")==0)||(tags==NULL))) {
	    			pch = strtok (tags,",");
				//MBM 
				//printf("tag = %s\n", pch);
				while (pch != NULL) {
					ptr=strchr(pch,'*');
					if (ptr==NULL) {
						knowntags->add(pch);
					} else {
						strncpy(dup,pch,(ptr-pch));
						dup[ptr-pch]='\0';
						starlist=Tags->getPartialList(dup);
						while ((starlist!=NULL) && (!(starlist->isEmpty())))
						{
							tmpch=starlist->remove();
							knowntags->add(tmpch);
							free(tmpch);
						}
					}
					//YV changed
					pch = strtok(NULL,",");
				}
			}
		        if (starlist!=NULL) {
			    starlist->removeAll();
			    delete starlist;
			    starlist=NULL;
			}
			#ifndef NOVERBOSE
			printf("Tags to search for %s\n",knowntags->toString());
			#endif
			linkedlist *tmpfiles,*tempforsuggest;
		        if (files!=NULL) {
			    files->removeAll();
			    delete files;
			    files=NULL;
			}
			files=search(knowntags);
			tmpfiles=files->copyList();
			if (suggestlist!=NULL) {
				suggestlist->removeAll();
				delete suggestlist;
				suggestlist=NULL;
			}

			if ((strcmp(segptr->command,HIERARCHICAL)==0))
				suggestlist=NULL;
			else
				suggestlist=suggest(tmpfiles);
				

			if (tmpfiles!=NULL) {
				tmpfiles->removeAll();
				delete tmpfiles;
				tmpfiles=NULL;
			}
			//printf("Files %s\n",files->toString());
			//printf("Sugge %s\n",suggestlist->toString());
			sprintf(message,"%s\n",((files==NULL)||(files->isEmpty()))?"FAILED":"SUCCESS");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			result=files;
		        if (knowntags!=NULL) {
			    knowntags->removeAll();
			    delete knowntags;
			    knowntags=NULL;
			}
		}
		// List n files with tag
		else if (strcmp(segptr->command,"listNfiles")==0) {
			linkedlist *knowntags=new linkedlist();
			linkedlist *starlist=NULL;
			linkedlist *files=NULL;
			sprintf(message,"List N files with tags : %s\n",segptr->param1);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			char *ptr,*pch;
			char dup[200];
			char tags[300];
			char *tmpch;
			strcpy(tags,segptr->param1);
			if (!((strcmp(tags,"")==0)||(tags==NULL))) {
	    			pch = strtok (tags," ");
				while (pch != NULL) {
					ptr=strchr(pch,'*');
					if (ptr==NULL) {
						knowntags->add(pch);
					} else {
						strncpy(dup,pch,(ptr-pch));
						dup[ptr-pch]='\0';
						starlist=Tags->getPartialList(dup);
						while ((starlist!=NULL) && (!(starlist->isEmpty())))
						{
							tmpch=starlist->remove();
							knowntags->add(tmpch);
							free(tmpch);
						}
					}
					pch = strtok(NULL," ");
				}
			}
			if (starlist!=NULL) {
				starlist->removeAll();
				delete starlist;
				starlist=NULL;
			}
			#ifndef NOVERBOSE
			printf("Known Tags : %s \n",knowntags->toString());
			#endif
			linkedlist *tmpfiles;
			if (files!=NULL) {
				files->removeAll();
				delete files;
				files=NULL;
			}
			files=listNfiles(knowntags,(strcmp(segptr->param2,"FIRST")==0));
		        if (starlist!=NULL) {
			    starlist->removeAll();
			    delete starlist;
			    starlist=NULL;
			}
		        if (knowntags!=NULL) {
			    knowntags->removeAll();
			    delete knowntags;
			    knowntags=NULL;
			}
			#ifndef NOVERBOSE
			printf("Got the files \n");
			#endif
			if ((files!=NULL)&&(!files->isEmpty())) {
				tmpfiles=files->copyList();
				suggestlist=suggest(tmpfiles);
			} else
				suggestlist=new linkedlist();

			if (suggestlist!=NULL) {
				suggestlist->removeAll();
				delete suggestlist;
				suggestlist=NULL;
			}

			sprintf(message,"%s\n",((files==NULL)||(files->isEmpty()))?"FAILED":"SUCCESS");
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
			result=files;
		}

		else if (strcmp(segptr->command,"getsuggestions")==0) {
			if(suggestlist!=NULL){
				result=suggestlist->copyList();
				//suggestlist->list();
			}
		}

		/*! - endserver --> Command to stop the server */
		else if (strcmp(segptr->command,"endserver")==0) {
			shutdown=true;
		}

		else {
			sprintf(message,"Unknown Command : %s\n",segptr->command);
			#ifndef NOVERBOSE
			printf("%s",message);
			#endif
		}			
		#ifndef NOVERBOSE
		printf("Request Completed\n");
		#endif


//------------------------------------------------------------------------------
// Operation performed and result stored in result
//------------------------------------------------------------------------------
#ifndef NOVERBOSE
		printf("Processing Over\n");
		printf("Sending Result\n");
#endif		
		if (result==NULL)
			result = new linkedlist();
		if (!result->isEmpty())
			result->add((char*) "FILLER!!! Wont be used");

		strcpy(segptr->result,"SENDING");
		segptr->moredata=true;
		int c;
		char *tmpres;
		while (!result->isEmpty()) {
			for (c=0;c<DTP;c++) {
				tmpres=result->remove();
				strcpy(segptr->data[c],tmpres);
				free(tmpres);
				if (result->isEmpty())
					break;
			}
			for (;c<DTP;c++) 
				strcpy(segptr->data[c],"");
			sem_signal(client_sem);
			sem_wait(server_sem);
			if (segptr->abort) {
				#ifndef NOIPCNOTIFICATION
				printf("Client aborted!!\n");
				#endif
        			sarray[client_sem]=0; 
				sarray[server_sem]=1;
				sarray[anyclient]=0; 	
				break;
			}
		}
		strcpy(segptr->result,"OVER");
		#ifndef NOIPCNOTIFICATION
		printf("\nProcessing over, Releasing block to client!!!\n");
		#endif
		if (segptr->abort) {
			#ifndef NOIPCNOTIFICATION
			printf("Client aborted!!\n");
			#endif
			strcpy(segptr->command,""); // Nullify the command
        		sarray[client_sem]=0; 
			sarray[server_sem]=1;
			sarray[anyclient]=0; 	
			continue;
		}
		//printf("Results Sent. Notifying Client\n");
		sem_signal(client_sem);	
		if (result!=NULL) {
			result->removeAll();
			delete result;
			result=NULL;
		}
		if (shutdown)
			closeserver();
		//printf("Client Notified\n");
	}
}

void closeserver()
{
	stopwatch sttime;
	sttime.start();
	if (booting==false)
		writetofile();
	sttime.stop();
	printf("Shutdown Server took %d seconds\n",(int)sttime.reading());
	printf("Trying to shutdown server,free up shared memory\n");
        shmctl(shmid, IPC_RMID, 0);
	semctl(semid, IPC_RMID, 0);
	printf("Destroying Index...\n");
	destroy_tries();
	printf("Server Succesfully shutdown!\n");
	exit(0);
}

void sigint_handler(int sig){
	printf("Interrupt recieved!!!\nShutting down server\n");
	signal(SIGINT,sigint_handler);
	closeserver();
}

/*! function to destroy both the file and tag tries */
void destroy_tries() {

	nPatriciaTrieNode<nPatriciaTrie<char>*> *t;
	linkedlist *lst;
	char *st;

	/*!- Get each file in the whole file trie and delete memory for it */
	lst=File->getList();
	while ((lst!=NULL) && (!lst->isEmpty())) {
		st=lst->remove();
		t = File->LookupNode(st);
		delete t->GetData();
		t->SetData(NULL);
		free(st);
	}
	delete lst;

	/*!- Get each tag in the whole tag trie and delete memory for it */
	lst=Tags->getList();
	while ((lst!=NULL) && (!lst->isEmpty())) {
		st=lst->remove();
		t = Tags->LookupNode(st);
		delete t->GetData();
		t->SetData(NULL);
		free(st);
	}
	delete lst;

	File->deleteMemory();
	Tags->deleteMemory();

	delete File;
	delete Tags;
	File=NULL;
	Tags=NULL;

	free (commonheadkey);
}
